package com.example;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.EventExecutorGroup;
import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

/**
 * @author xmk
 */
public class MyNetty {

    @Test
    public void byteBuf() {
//        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(8, 20);
//        ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.buffer(8, 20);
        ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.heapBuffer(8, 20);
        print(buf);

        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
    }

    private void print(ByteBuf buf) {
        System.out.println("buf.isReadable() == " + buf.isReadable());
        System.out.println("buf.readerIndex() == " + buf.readerIndex());
        System.out.println("buf.readableBytes() == " + buf.readableBytes());
        System.out.println("buf.isWritable() == " + buf.isWritable());
        System.out.println("buf.writerIndex() == " + buf.writerIndex());
        System.out.println("buf.writableBytes() == " + buf.writableBytes());
        System.out.println("buf.capacity() == " + buf.capacity());
        System.out.println("buf.maxCapacity() == " + buf.maxCapacity());
        System.out.println("buf.isDirect() == " + buf.isDirect());
        System.out.println("buf.isReadOnly() == " + buf.isReadOnly());
        System.out.println("================== ");
    }

    @Test
    public void nioEventloopGroup() throws IOException {
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup(2);
        eventExecutors.execute(() -> {
            while (true) {
                System.out.println("hello event 1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        eventExecutors.execute(() -> {
            while (true) {
                System.out.println("hello event 2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.in.read();
    }

    @Test
    public void client() throws InterruptedException {

        NioEventLoopGroup eventExecutors = new NioEventLoopGroup(1);

        NioSocketChannel client = new NioSocketChannel();

        eventExecutors.register(client);

        ChannelPipeline pipeline = client.pipeline();
        pipeline.addLast(new MyInHandler());

        ChannelFuture connect = client.connect(new InetSocketAddress("111.229.141.32", 9090));
        ChannelFuture sync = connect.sync();

        ByteBuf buf = Unpooled.copiedBuffer("hello event".getBytes());
        ChannelFuture sent = client.writeAndFlush(buf);
        ChannelFuture sync1 = sent.sync();

        sync.channel().closeFuture().sync();

        System.out.println("end...");
    }

    @Test
    public void nettyClient() throws InterruptedException {

        NioEventLoopGroup eventExecutors = new NioEventLoopGroup(1);

        Bootstrap bs = new Bootstrap();

        bs.group(eventExecutors)
                .channel(NioSocketChannel.class)
                .handler(new MyInHandler())
                .connect(new InetSocketAddress("111.229.141.32", 9090))
                .sync()
                .channel()
                .closeFuture().sync();
    }

    @Test
    public void nettyClientSend() throws InterruptedException {

        NioEventLoopGroup eventExecutors = new NioEventLoopGroup(1);

        Bootstrap bs = new Bootstrap();

        ChannelFuture connect = bs.group(eventExecutors)
                .channel(NioSocketChannel.class)
                .handler(new MyInHandler())
                .connect(new InetSocketAddress( "localhost",9000));

        Channel client = connect.sync().channel();

        ByteBuf buf = Unpooled.copiedBuffer("QUERY TIME ORDER".getBytes());
        ChannelFuture sent = client.writeAndFlush(buf);
        sent.sync();

        client.closeFuture().sync();
    }

    @Test
    public void server() throws InterruptedException {

        NioEventLoopGroup loopGroup = new NioEventLoopGroup(1);

        NioServerSocketChannel server = new NioServerSocketChannel();

        loopGroup.register(server);

        ChannelPipeline pipeline = server.pipeline();

        pipeline.addLast(new MyAcceptHandler(loopGroup, new MyInHandler()));

        ChannelFuture bind = server.bind(new InetSocketAddress(9090));

        ChannelFuture sync = bind.sync();


        sync.channel().closeFuture().sync();
        System.out.println("server close...");

    }

    @Test
    public void nettyServer() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);

        ServerBootstrap bs = new ServerBootstrap();

        bs.group(group, group)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new MyInHandler());
                    }
                })
                .bind(new InetSocketAddress(9090))
                .sync()
                .channel()
                .closeFuture()
                .sync();

    }

    @ChannelHandler.Sharable
    private class MyInHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
//            super.channelRegistered(ctx);
            System.out.println("client register...");
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf buf = (ByteBuf) msg;
//            CharSequence str = buf.readCharSequence(buf.readableBytes(), Charset.defaultCharset());
            CharSequence str = buf.getCharSequence(0, buf.readableBytes(), Charset.defaultCharset());
            System.out.println(str);
//            ctx.writeAndFlush(buf);

            //            super.channelRead(ctx, msg);
        }
    }

    private class MyAcceptHandler extends ChannelInboundHandlerAdapter {
        private EventLoopGroup loopGroup;
        private ChannelHandler inHandler;

        MyAcceptHandler(EventLoopGroup loopGroup, ChannelHandler inHandler) {
            this.loopGroup = loopGroup;
            this.inHandler = inHandler;
        }

        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println("server register....");
            //            super.channelRegistered(ctx);
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("server active...");
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            NioSocketChannel client = (NioSocketChannel) msg;

            loopGroup.register(client);

            ChannelPipeline pipeline = client.pipeline();

            pipeline.addLast(inHandler);
//            pipeline.addLast(new MyInHandler());


            //            super.channelRead(ctx, msg);
        }
    }
}
